/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.rmi; import java.io.IOException; import java.text.MessageFormat; import java.util.*; import org.openide.*; import org.openide.filesystems.*; import org.openide.loaders.*; import org.openide.util.actions.SystemAction; import org.openide.util.*; import org.openide.actions.*; import org.openide.src.*; import org.netbeans.modules.rmi.settings.*; import org.netbeans.modules.java.*; /** The DataLoader for RMIDataObjects. * This class is final only for performance reasons, * can be happily unfinaled if desired. * * @author Martin Ryzl, Dafe Simonek */ public final class RMIDataLoader extends JavaDataLoader { transient private static final boolean DEBUG = false; /** Serial version UID. */ static final long serialVersionUID = -3325329576021256358L; /** Resource bundle. */ static final ResourceBundle bundle = NbBundle.getBundle(RMIDataLoader.class); /** Extension for rmi object. */ public static final String RMI_EXTENSION = "rmi"; // NOI18N /** Default extension for remote implementation. */ public static final String IMPL_SUFFIX = "Impl"; // NOI18N /** Inner class divider. */ static final char INNER_CLASS_DIVIDER = '$'; /** Yes option. */ public static final String OPTION_YES = bundle.getString("CTL_OPTION_YES"); // NOI18N /** Yes to all option. */ public static final String OPTION_YES_ALL = bundle.getString("CTL_OPTION_YES_ALL"); // NOI18N /** No option. */ public static final String OPTION_NO = bundle.getString("CTL_OPTION_NO"); // NOI18N /** No to all option. */ public static final String OPTION_NO_ALL = bundle.getString("CTL_OPTION_NO_ALL"); // NOI18N /** List of extensions recognized by this loader */ private static ExtensionList extensions; static private Parsing.Listener parsingListener = null; static private RMISettings settings = (RMISettings) RMISettings.findObject(RMISettings.class, true); /** Storage for reusableDispose. Contains (FileObject, RMIDataObject) pairs. */ static HashMap reusableSet = new HashMap(8); /** All objects. */ private static WeakSet allSet = new WeakSet(64); /** Creates a new RMIDataLoader * */ public RMIDataLoader() { this (RMIDataObject.class); } /** Creates a new RMIDataLoader * */ public RMIDataLoader(Class recognizedObject) { super (recognizedObject); } protected void initialize () { setParsingListener(); settings.addPropertyChangeListener(new SettingsListener()); setDisplayName(bundle.getString("PROP_RMILoader_Name")); // NOI18N setActions (new SystemAction [] { SystemAction.get(OpenAction.class), SystemAction.get(CustomizeBeanAction.class), SystemAction.get(FileSystemAction.class), null, SystemAction.get(CompileAction.class), null, SystemAction.get(ExecuteAction.class), null, SystemAction.get(CutAction.class), SystemAction.get(CopyAction.class), SystemAction.get(PasteAction.class), null, SystemAction.get(DeleteAction.class), SystemAction.get (RenameAction.class), null, SystemAction.get(SaveAsTemplateAction.class), null, SystemAction.get(ToolsAction.class), SystemAction.get(PropertiesAction.class), }); } /** For a given file finds a primary file. * @param fo the file to find primary file for * * @return the primary file for the file or null if the file is not * recognized by this loader */ protected FileObject findPrimaryFile (FileObject fo) { // ignore folder if (fo.isFolder()) return null; final String ext = fo.getExt(), fname = fo.getName(); String name = null; FileObject primaryFO; boolean rmiFile = false; // int index; // only for .class, .java or *.rmi if (ext.equals(CLASS_EXTENSION) || ext.equals(JAVA_EXTENSION) || ext.equals(RMI_EXTENSION)) { // check for inner class if ((index = fname.indexOf(INNER_CLASS_DIVIDER)) != -1) { name = fname.substring(0, index); } else { if (isHideStubs()) name = checkStub(fname); if (name == null) name = fname; } } // if .rmi doesn't exist it is not an RMI object if ((rmiFile = (fo.getParent().getFileObject(name, RMI_EXTENSION)) != null)) { if ((primaryFO = fo.getParent().getFileObject(name, JAVA_EXTENSION)) != null) { return primaryFO; } } // not recognized return null; } /** Check stub. * @return name of the main file or null */ static String checkStub(final String name) { String[] formats = settings.getStubFormats(); for(int i = 0; i < formats.length; i++) { MessageFormat fmt = new MessageFormat(formats[i]); try { Object[] objs = fmt.parse(name); if (objs != null) return objs[0].toString(); } catch (java.text.ParseException ex) { // continue } } return null; } /** */ protected boolean isHideStubs() { return settings.isHideStubs(); } /** Creates the right data object for given primary file. * It is guaranteed that the provided file is realy primary file * returned from the method findPrimaryFile. * * @param primaryFile the primary file * @return the data object for this file * @exception DataObjectExistsException if the primary file already has data object */ protected MultiDataObject createMultiObject (FileObject primaryFile) throws DataObjectExistsException, IOException { RMIDataObject rmido = (RMIDataObject) reusableSet.remove(primaryFile); if (rmido == null) { rmido = new RMIDataObject(primaryFile, this); allSet.add(rmido); } return rmido; } /** Creates the right primary entry for given primary file. * * @param primaryFile primary file recognized by this loader * @return primary entry for that file */ protected MultiDataObject.Entry createPrimaryEntry (MultiDataObject obj, FileObject primaryFile) { return new RMIFileEntry(obj, primaryFile); } /** Creates right secondary entry for given file. The file is said to * belong to an object created by this loader. * * @param secondaryFile secondary file for which we want to create entry * @return the entry */ protected MultiDataObject.Entry createSecondaryEntry (MultiDataObject obj, FileObject secondaryFile) { if (secondaryFile.getExt().equals(RMI_EXTENSION)) return new FileEntry(obj, secondaryFile); return super.createSecondaryEntry (obj, secondaryFile); } /** @return The list of extensions this loader recognizes * (default list constains class, ser extensions) */ public ExtensionList getExtensions () { if (extensions == null) { extensions = new ExtensionList(); extensions.addExtension(RMI_EXTENSION); extensions.addExtension(CLASS_EXTENSION); extensions.addExtension(JAVA_EXTENSION); } return extensions; } /** Sets the extension list for this data loader. * @param ext new list of extensions. */ public void setExtensions(ExtensionList ext) { extensions = ext; } /** This entry defines the format for replacing the text during * instantiation the data object. */ public class RMIFileEntry extends JavaDataLoader.JavaFileEntry { /** Serial version UID. */ static final long serialVersionUID = 2704205254221237611L; /** Creates new JavaFileEntry */ protected RMIFileEntry(MultiDataObject obj, FileObject file) { super(obj, file); } protected void modifyMap(Map map, FileObject target, String n, String e) { super.modifyMap(map, target, n, e); RMIDataObject obj = (RMIDataObject) getDataObject(); String iName = n; int i; // interface name if ((i = iName.indexOf(IMPL_SUFFIX)) != -1) iName = iName.substring(0, i); else iName = RMIHelper.REMOTE; map.put("INTERFACENAME", iName); // NOI18N map.put("STRING", "\""); // NOI18N // string versions of package and name String string; if ((string = (String) map.get("NAME")) != null) { // NOI18N map.put("NAMESTRING", "\"" + string + "\""); // NOI18N } if ((string = (String) map.get("PACKAGE")) != null) { // NOI18N map.put("PACKAGESTRING", "\"" + string + "\""); // NOI18N } if ((string = getFSPath(getFile())) != null) { map.put("FSPATH", string); // NOI18N } } } /** Returns path to the filesystem of the file object. * @return path to the filesystem*/ public static String getFSPath(FileObject fo) { final StringBuffer sb = new StringBuffer(); // getPackage try { FileSystem fs = fo.getFileSystem(); fo.getFileSystem().prepareEnvironment(new FileSystem.Environment() { public void addClassPath(String element) { sb.append(element); } }); return sb.toString(); } catch (FileStateInvalidException ex) { } catch (EnvironmentNotSupportedException ex) { } return null; } /** Mark JDO as RMI. * @param jdo - data object to mark * @param set - if true, set it as RMI, if false, unset */ public static void markRMI(JavaDataObject jdo, boolean set) throws IOException, java.beans.PropertyVetoException { try { if (jdo == null) return; FileObject fo = jdo.getPrimaryFile(); if (set) { try { // mark it! fo.getParent().createData(fo.getName(), RMI_EXTENSION); } catch (Exception ex) { // ignore it } if (!(jdo instanceof RMIDataObject)) { if (FileUtil.findBrother(fo, RMI_EXTENSION) != null) jdo.setValid(false); } } else { if (jdo instanceof RMIDataObject) { FileLock lock = null; try { FileObject rmi = FileUtil.findBrother(fo, RMI_EXTENSION); lock = rmi.lock(); rmi.delete(lock); } catch (Exception ex) { // ignore it } finally { if (lock != null) lock.releaseLock(); } if (FileUtil.findBrother(fo, RMI_EXTENSION) == null) jdo.setValid(false); } } } catch (java.beans.PropertyVetoException ex) { ex.printStackTrace(); throw ex; } } /** Add parsing listener. */ protected static void setParsingListener() { boolean isEnabled = settings.isDetectRemote(); if (isEnabled) { if (parsingListener == null) { parsingListener = new ParsingListener(); Parsing.addParsingListener(parsingListener); } } else { // not enabled if (parsingListener != null) { Parsing.removeParsingListener(parsingListener); parsingListener = null; } } } /** The listener interface for everybody who want to control all * parsed JavaDataObjects. */ protected static class ParsingListener implements Parsing.Listener { volatile NotifyDescriptor nd = null; final MessageFormat format = new MessageFormat(bundle.getString("FMT_CONVERT_MESSAGE")); // NOI18N final Object[] params = new Object[1]; /** Method which is called everytime when some object is parsed. * @param evt The event with the details. */ public void objectParsed(final Parsing.Event evt) { RequestProcessor.postRequest(new Runnable() { public void run() { try { // dont forget evt until everything is done ! final JavaDataObject jdo = evt.getJavaDataObject(); // only non RMI if (jdo instanceof RMIDataObject) return; SourceElement se = jdo.getSource(); final ClassElement ce = se.getClass(Identifier.create(jdo.getPrimaryFile().getPackageName('.'))); if (ce.isClass()) { if (RMIHelper.implementsClass(ce, RMIHelper.REMOTE)) { // ask user if necessary, eventually modify settings if (getConfirm()) { RequestProcessor.postRequest(new Runnable() { public void run() { try { Object result = TopManager.getDefault().notify(getDescriptor(ce)); if (result == OPTION_YES) { markRMI(jdo, true); } if (result == OPTION_YES_ALL) { markRMI(jdo, true); setConfirm(false); } if (result == OPTION_NO_ALL) { setDetect(false); } } catch (Exception ex) { TopManager.getDefault().notifyException(ex); } } }); } else { // confirm without asking try { markRMI(jdo, true); } catch (Exception ex) { TopManager.getDefault().notifyException(ex); } } } } } catch (NullPointerException ex) { // ignore it } } }); } /** Getter for confirm. * @return true if the action should be confirmed. */ protected boolean getConfirm() { return settings.isConfirmConvert(); } /** Setter for confirm. * @param confirm if true, subsequent actions should be confirmed */ protected void setConfirm(boolean confirm) { settings.setConfirmConvert(confirm); } /** Setter for detect. * @param detect if true, detect all parsing events and check for implements * Remote */ protected void setDetect(boolean detect) { settings.setDetectRemote(detect); } /** Get notify descriptor. * @return a NotifyDesriptor for the confirmation dialog */ protected NotifyDescriptor getDescriptor(ClassElement ce) { if (nd == null) { nd = new NotifyDescriptor( "", // NOI18N bundle.getString("CTL_CONFIRMATION_TITLE"), // NOI18N NotifyDescriptor.DEFAULT_OPTION, NotifyDescriptor.QUESTION_MESSAGE, new Object[] { OPTION_YES, OPTION_YES_ALL, OPTION_NO, OPTION_NO_ALL }, null ); } params[0] = ce.getName().getFullName(); nd.setMessage( format.format(params) ); return nd; } } /** Listens on RMISettings. */ private static class SettingsListener implements java.beans.PropertyChangeListener { public void propertyChange(final java.beans.PropertyChangeEvent ev) { if (RMISettings.PROP_HIDE_STUBS.equals(ev.getPropertyName())) { RequestProcessor.postRequest(new HideStubChanger()); } if (RMISettings.PROP_DETECT_REMOTE.equals(ev.getPropertyName())) { setParsingListener(); } } } /** Update all RMI objects. */ private static class HideStubChanger implements Runnable { public void run() { final boolean hideStubs = settings.isHideStubs(); Set set = new HashSet(allSet); // clone Iterator it = set.iterator(); while (it.hasNext()) { RMIDataObject rmido = (RMIDataObject) it.next(); if (rmido != null) { if (hideStubs) rmido.aquireStubs(); else rmido.dropStubs(); } } } } } /* * <<Log>> * 33 Gandalf-post-FCS1.29.1.2 4/17/00 Martin Ryzl data loader now ignores * folders * 32 Gandalf-post-FCS1.29.1.1 3/20/00 Martin Ryzl localization * 31 Gandalf-post-FCS1.29.1.0 3/8/00 Martin Ryzl hide stubs feature * 30 src-jtulach1.29 1/28/00 Martin Ryzl one semicolon removed * (problems with compilation by fastjavac) * 29 src-jtulach1.28 1/25/00 Martin Ryzl objects recognitiot * fixed * 28 src-jtulach1.27 1/24/00 Martin Ryzl compilation of inner * classes added * 27 src-jtulach1.26 1/5/00 Jaroslav Tulach Change in notify * descriptor. * 26 src-jtulach1.25 11/27/99 Patrik Knakal * 25 src-jtulach1.24 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 24 src-jtulach1.23 10/15/99 Martin Ryzl BUG #4519 fixed * 23 src-jtulach1.22 10/13/99 Petr Kuzel HOTFIXed due to compile * errs * 22 src-jtulach1.21 10/13/99 Martin Ryzl ugly deadlock in * ParsingListener removed * 21 src-jtulach1.20 10/13/99 Martin Ryzl markRMI corrected * 20 src-jtulach1.19 10/12/99 Martin Ryzl debug info removed * 19 src-jtulach1.18 10/12/99 Martin Ryzl Automatic detection of * RMI * 18 src-jtulach1.17 10/1/99 Jaroslav Tulach Loaders extends * SharedClassObject * 17 src-jtulach1.16 8/31/99 Ian Formanek Correctly provides * FileSystemAction on its data objects * 16 src-jtulach1.15 7/27/99 Martin Ryzl * 15 src-jtulach1.14 7/20/99 Martin Ryzl * 14 src-jtulach1.13 6/9/99 Ian Formanek ToolsAction * 13 src-jtulach1.12 6/9/99 Ian Formanek ---- Package Change To * org.openide ---- * 12 src-jtulach1.11 5/12/99 Martin Ryzl * 11 src-jtulach1.10 5/5/99 Martin Ryzl * 10 src-jtulach1.9 5/4/99 Martin Ryzl * 9 src-jtulach1.8 4/23/99 Martin Ryzl debug info removed * 8 src-jtulach1.7 4/21/99 Martin Ryzl * 7 src-jtulach1.6 4/20/99 Martin Ryzl * 6 src-jtulach1.5 4/15/99 Martin Ryzl * 5 src-jtulach1.4 4/15/99 Martin Ryzl * 4 src-jtulach1.3 3/26/99 Ian Formanek Fixed use of obsoleted * NbBundle.getBundle (this) * 3 src-jtulach1.2 3/23/99 Martin Ryzl * 2 src-jtulach1.1 3/19/99 Martin Ryzl * 1 src-jtulach1.0 3/17/99 David Simonek * $ */